package com.example.javasample.controller;

import com.example.javasample.utils.MathUtils;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

// MathParser.org-mXparser
import org.mariuszgromada.math.mxparser.Argument;
import org.mariuszgromada.math.mxparser.Expression;

// Nashorn
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

// JEP
import org.nfunk.jep.JEP;

// Janino
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.IExpressionEvaluator;
import org.codehaus.janino.ExpressionEvaluator;

// Jexl
import org.apache.commons.jexl3.*;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * ExpressionController
 *
 * @author Kylin
 */
@Tag(name = "计算引擎")
@RestController
@RequestMapping("/expression")
public class ExpressionController {

    // MathParser.org-mXparser
    // 在类初始化时添加MathParser.org-mXparser许可证声明
    static {
        org.mariuszgromada.math.mxparser.License.iConfirmNonCommercialUse("JavaSample");
    }

    public static Number mxparserCalc(String expressionString) {
        return new Expression(expressionString).calculate();
    }

    @ApiOperationSupport(order = 1)
    @Operation(summary = "MathParser.org-mXparser计算")
    @PostMapping(value = "/mxparser")
    public Number mxparser(String expressionString) {
        return mxparserCalc(expressionString);
    }

    public static void addMxparserResult(Map<String, Number> resultMap, String expressionString) {
        resultMap.put(expressionString, mxparserCalc(expressionString));
    }

    @ApiOperationSupport(order = 1)
    @Operation(summary = "MathParser.org-mXparser示例")
    @PostMapping(value = "/mxparser/example")
    public Map<String, Number> mxparserExample() {
        Map<String, Number> result = new LinkedHashMap<>();
        addMxparserResult(result, "1 / 3");
        addMxparserResult(result, "1 + 2 * 3 / 4 - 5");
        addMxparserResult(result, "1 + 2 × 3 ÷ 4 - 5");
        addMxparserResult(result, "100 ⨉ 5%");
        // 幂运算和平方根
        addMxparserResult(result, "2 ^ 3 + sqrt(16)");
        // 绝对值
        addMxparserResult(result, "abs(-1)");
        // 常用函数
        addMxparserResult(result, "min(1, 2, 3, 4, 5)");
        addMxparserResult(result, "max(1, 2, 3, 4, 5)");
        addMxparserResult(result, "mean(1, 2, 3, 4, 5)");
        addMxparserResult(result, "add(1, 2, 3, 4, 5)");
        addMxparserResult(result, "sum(n, 1, 3, n^2)");
        addMxparserResult(result, "0 > 1");
        // 三元函数
        addMxparserResult(result, "if(1 > 0, 99, 0)");
        // 三角函数
        addMxparserResult(result, "sin(pi / 6) + sin(π / 6)");
        addMxparserResult(result, "cos(pi / 3)");
        addMxparserResult(result, "tan(pi / 4)");
        addMxparserResult(result, "tan(45 * pi / 180)");
        // 反三角函数
        addMxparserResult(result, "asin(0.5)");
        addMxparserResult(result, "acos(0.5)");
        addMxparserResult(result, "atan(1)");
        // 自定义含变量的表达式，然后将变量值代入计算，例如求解二次方程 ax²+bx+c=0 的根
        Expression quadratic = new Expression("(-b + sqrt(b^2 - 4*a*c)) / (2*a)",
                new Argument("a = 1"), new Argument("b = -5"), new Argument("c = 6"));
        result.put(quadratic.getExpressionString() + "【a=1,b=-5,c=6】", quadratic.calculate());
        return result;
    }


    // Nashorn
    public static Number nashornCalc(String expressionString) {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        try {
            Object result = engine.eval(expressionString);
            System.out.println(expressionString + " = " + result);
            return (Number) result;
        } catch (ScriptException se) {
            se.printStackTrace();
            return null;
        }
    }

    @ApiOperationSupport(order = 2)
    @Operation(summary = "Nashorn计算")
    @PostMapping(value = "/nashorn")
    public Number nashorn(String expressionString) {
        return nashornCalc(expressionString);
    }

    public static void addNashornResult(Map<String, Number> resultMap, String expressionString) {
        resultMap.put(expressionString, nashornCalc(expressionString));
    }

    @ApiOperationSupport(order = 2)
    @Operation(summary = "Nashorn示例")
    @PostMapping(value = "/nashorn/example")
    public Map<String, Number> nashornExample() {
        Map<String, Number> result = new LinkedHashMap<>();
        addNashornResult(result, "1 / 3");
        addNashornResult(result, "1 + 2 * 3 / 4 - 5");
        // 幂运算和平方根
        addNashornResult(result, "Math.pow(2, 3) + Math.sqrt(16)");
        addNashornResult(result, "Math.abs(-1)");
        addNashornResult(result, "Math.min(1, 2, 3, 4, 5)");
        addNashornResult(result, "Math.max(1, 2, 3, 4, 5)");
        // 三角函数
        addNashornResult(result, "Math.sin(Math.PI/6)");
        addNashornResult(result, "Math.cos(Math.PI/3)");
        addNashornResult(result, "Math.tan(Math.PI/4)");
        // 反三角函数
        addNashornResult(result, "Math.asin(0.5)");
        addNashornResult(result, "Math.acos(0.5)");
        addNashornResult(result, "Math.atan(1)");
        // 自定义含变量的表达式，然后将变量值代入计算，例如求解二次方程 ax²+bx+c=0 的根
        String rootExpressioString = "(-b + Math.sqrt(Math.pow(b,2) - 4*a*c)) / (2*a)";
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.put("a", 1);
        engine.put("b", -5);
        engine.put("c", 6);
        try {
            result.put(rootExpressioString + "【a=1,b=-5,c=6】", (Number) engine.eval(rootExpressioString));
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return result;
    }


    // JEP
    public static Number jepCalc(String expressionString) {
        JEP jep = new JEP();
        // 引入标准函数（必须先引入函数，才可执行对应内置方法）
        jep.addStandardFunctions();
        // 计算表达式
        jep.parseExpression(expressionString);
        return jep.getValue();
    }

    public static Number jepCalc(String expressionString, Map<String, Number> contextMap) {
        JEP jep = new JEP();
        // 引入标准函数（必须先引入函数，才可执行对应内置方法）
        jep.addStandardFunctions();
        // 设置变量
        for (Map.Entry<String, Number> entry : contextMap.entrySet()) {
            jep.addVariable(entry.getKey(), entry.getValue().doubleValue());
        }
        // 计算表达式
        jep.parseExpression(expressionString);
        return jep.getValue();
    }

    @ApiOperationSupport(order = 3)
    @Operation(summary = "JEP计算")
    @PostMapping(value = "/jep")
    public Number jep(String expressionString) {
        return jepCalc(expressionString);
    }

    public static void addJepResult(Map<String, Number> resultMap, String expressionString) {
        resultMap.put(expressionString, jepCalc(expressionString));
    }

    @ApiOperationSupport(order = 3)
    @Operation(summary = "JEP示例")
    @PostMapping(value = "/jep/example")
    public Map<String, Number> jepExample() {
        Map<String, Number> result = new LinkedHashMap<>();
        addJepResult(result, "1 / 3");
        addJepResult(result, "1 + 2 * 3 / 4 - 5");
        addJepResult(result, "sum(1, 2, 3, 4, 5)");
        // 幂运算和平方根
        addJepResult(result, "2 ^ 3");
        addJepResult(result, "sqrt(16)");
        addJepResult(result, "abs(-1)");
        addJepResult(result, "min(1, 2, 3, 4, 5)");
        addJepResult(result, "max(1, 2, 3, 4, 5)");
        // 三角函数
        addJepResult(result, "sin(3.1415/6)");
        addJepResult(result, "cos(3.1415/3)");
        addJepResult(result, "tan(3.1415/4)");
        // 反三角函数
        addJepResult(result, "asin(0.5)");
        addJepResult(result, "acos(0.5)");
        addJepResult(result, "atan(1)");
        // 自定义含变量的表达式，然后将变量值代入计算，例如求解二次方程 ax²+bx+c=0 的根
        String rootExpressioString = "(-b + sqrt(b^2 - 4*a*c)) / (2*a)";
        result.put(rootExpressioString + "【a=1,b=-5,c=6】", jepCalc(rootExpressioString,
                Map.of("a", 1, "b", -5, "c", 6)));
        return result;
    }


    // Janino
    public static Number janinoCalc(String expressionString) {
        try {
            IExpressionEvaluator evaluator = new ExpressionEvaluator();
            evaluator.cook(expressionString);
            return (Number) evaluator.evaluate();
        } catch (CompileException | InvocationTargetException cie) {
            cie.printStackTrace();
            return null;
        }
    }

    @ApiOperationSupport(order = 4)
    @Operation(summary = "Janino计算")
    @PostMapping(value = "/janino")
    public Number janino(String expressionString) {
        return janinoCalc(expressionString);
    }

    public static void addJaninoResult(Map<String, Number> resultMap, String expressionString) {
        resultMap.put(expressionString, janinoCalc(expressionString));
    }

    @ApiOperationSupport(order = 4)
    @Operation(summary = "Janino示例")
    @PostMapping(value = "/janino/example")
    public Map<String, Number> janinoExample() {
        Map<String, Number> result = new LinkedHashMap<>();
        addJaninoResult(result, "1 / 3");
        addJaninoResult(result, "1 + 2 * 3 / 4 - 5");
        addJaninoResult(result, "sum(1, 2, 3, 4, 5)");
        // 幂运算和平方根
        addJaninoResult(result, "2 ^ 3");
        addJaninoResult(result, "sqrt(16)");
        addJaninoResult(result, "abs(-1)");
        addJaninoResult(result, "min(1, 2, 3, 4, 5)");
        addJaninoResult(result, "max(1, 2, 3, 4, 5)");
        // 三角函数
        addJaninoResult(result, "sin(3.1415/6)");
        addJaninoResult(result, "cos(3.1415/3)");
        addJaninoResult(result, "tan(3.1415/4)");
        // 反三角函数
        addJaninoResult(result, "asin(0.5)");
        addJaninoResult(result, "acos(0.5)");
        addJaninoResult(result, "atan(1)");
        // 自定义含变量的表达式，然后将变量值代入计算，例如求解二次方程 ax²+bx+c=0 的根
        String rootExpressioString = "(-b + sqrt(b^2 - 4*a*c)) / (2*a)";
        JEP jep = new JEP();
        // 引入标准函数（必须先引入函数，才可执行对应内置方法）
        jep.addStandardFunctions();
        // 设置变量
        jep.addVariable("a", 1);
        jep.addVariable("b", -5);
        jep.addVariable("c", 6);
        // 计算表达式
        jep.parseExpression(rootExpressioString);
        result.put(rootExpressioString + "【a=1,b=-5,c=6】", jep.getValue());
        return result;
    }


    // Jexl
    public static Number jexlCalc(String expressionString) {
        try {
            Map<String, Object> functions = new HashMap<>();

            // 创建上面类的对象，并起一个名称（下面会使用到名称）
            functions.put("Func", new MathUtils());
            JexlEngine jexl = new JexlBuilder()
                    .namespaces(Collections.singletonMap("math", MathUtils.class))
                    .create();
            JexlExpression expression = jexl.createExpression(expressionString);
            return (Number) expression.evaluate(new MapContext());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Number jexlCalc(String expressionString, Map<String, Number> contextMap) {
        try {
            JexlEngine jexl = new JexlBuilder()
                    .namespaces(Collections.singletonMap("math", MathUtils.class))
                    .create();
            JexlExpression expression = jexl.createExpression(expressionString);
            JexlContext context = new MapContext();
            for (Map.Entry<String, Number> entry : contextMap.entrySet()) {
                context.set(entry.getKey(), entry.getValue());
            }
            return (Number) expression.evaluate(context);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @ApiOperationSupport(order = 9)
    @Operation(summary = "Jexl计算")
    @PostMapping(value = "/jexl")
    public Number jexl(String expressionString) {
        return jexlCalc(expressionString);
    }

    public static void addJexlResult(Map<String, Number> resultMap, String expressionString) {
        resultMap.put(expressionString, jexlCalc(expressionString));
    }

    @ApiOperationSupport(order = 10)
    @Operation(summary = "Jexl示例")
    @PostMapping(value = "/jexl/example")
    public Map<String, Number> jexlExample() {
        Map<String, Number> result = new LinkedHashMap<>();
        addJexlResult(result, "1 / 3");
        addJexlResult(result, "1 + 2 * 3 / 4 - 5");
        addJexlResult(result, "sum(1, 2, 3, 4, 5)");
        // 幂运算和平方根
        addJexlResult(result, "math:pow(2,3)");
        addJexlResult(result, "sqrt(16)");
        addJexlResult(result, "abs(-1)");
        addJexlResult(result, "min(1, 2, 3, 4, 5)");
        addJexlResult(result, "max(1, 2, 3, 4, 5)");
        // 三角函数
        addJexlResult(result, "sin(3.1415/6)");
        addJexlResult(result, "cos(3.1415/3)");
        addJexlResult(result, "tan(3.1415/4)");
        // 反三角函数
        addJexlResult(result, "asin(0.5)");
        addJexlResult(result, "acos(0.5)");
        addJexlResult(result, "atan(1)");
        // 自定义含变量的表达式，然后将变量值代入计算，例如求解二次方程 ax²+bx+c=0 的根
        String rootExpressioString = "(-b + sqrt(b^2 - 4*a*c)) / (2*a)";
        result.put(rootExpressioString + "【a=1,b=-5,c=6】",
                jexlCalc(rootExpressioString, Map.of("a", 1, "b", -5, "c", 6)));
        return result;
    }

}
